package com.jindi.jacococli.biz.business.action;

import com.alibaba.nacos.api.naming.pojo.Instance;
import com.jindi.infra.tools.thread.EnhanceThreadPoolExecutor;
import com.jindi.jacococli.biz.business.client.MvnCompileClient;
import com.jindi.jacococli.biz.business.model.ExcelDataModel;
import com.jindi.jacococli.biz.business.model.ServiceProject;
import com.jindi.jacococli.biz.business.model.ServiceProjectList;
import com.jindi.jacococli.biz.business.queue.JacocoCliEvent;
import com.jindi.jacococli.biz.business.service.GitLabService;
import com.jindi.jacococli.biz.business.service.JacocoCliService;
import com.jindi.jacococli.biz.business.service.JacocoReportService;
import com.jindi.jacococli.biz.business.service.MailService;
import com.jindi.jacococli.biz.business.service.NacosFetchService;
import com.jindi.jacococli.biz.business.service.ReportCompressService;
import com.jindi.jacococli.biz.business.service.SonarService;
import com.jindi.jacococli.biz.business.task.ReportTaskManager;
import com.jindi.jacococli.biz.config.AppProperties;
import com.jindi.jacococli.biz.constant.Constant;
import com.jindi.jacococli.biz.utils.DateUtils;
import com.jindi.jacococli.biz.utils.ExcelUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static com.jindi.jacococli.biz.constant.Constant.*;

/**
 * 目前服务数为3300多个
 */

@Service
public class BatchReportServiceAction {

    public static final Logger LOGGER = LoggerFactory.getLogger(BatchReportServiceAction.class);
    @Resource
    private AppProperties appProperties;
    @Resource
    private GitLabService gitLabService;
    @Resource
    private MvnCompileClient mvnCompileClient;
    @Resource
    private NacosFetchService nacosFetchService;
    @Resource
    private JacocoCliService jacocoCliService;
    @Resource
    private JacocoReportService jacocoReportService;
    @Resource
    private ReportCompressService reportCompressService;
    @Resource
    private MailService mailService;
    @Resource
    private SonarService sonarService;

    @Resource
    @Qualifier("batchReportServiceTaskThreadPool")
    private EnhanceThreadPoolExecutor batchReportServiceTaskThreadPool;

    @Resource
    @Qualifier("subBatchReportServiceTaskThreadPool")
    private EnhanceThreadPoolExecutor subBatchReportServiceTaskThreadPool;

    public void doAction(ServiceProjectList serviceProjectList) {
        ReportTaskManager.add(serviceProjectList);
        batchReportServiceTaskThreadPool.execute(new BatchTask(serviceProjectList));
    }

    class BatchTask implements Runnable {
        private ServiceProjectList serviceProjectList;
        BatchTask(ServiceProjectList serviceProjectList) {
            this.serviceProjectList = serviceProjectList;
        }
        @Override
        public void run() {
            ReportTaskManager.setDescribe(serviceProjectList.getUuid(), "Running [开始执行中]");
            List<ServiceProject> services = serviceProjectList.getServices();
            List<ExcelDataModel> dataModel = new ArrayList<>();
            String attachDir = appProperties.getWorkSpace() + serviceProjectList.getUuid() + BACKSLASH + Constant.ATTACH + BACKSLASH;
            mkdir(attachDir);
            CountDownLatch cdl = new CountDownLatch(services.size());
            for (ServiceProject service : services) {
                submitToSub(serviceProjectList, cdl, () -> {
                    ExcelDataModel data = new ExcelDataModel();
                    dataModel.add(data);
                    data.setServices(service.getServiceName());
                    data.setDate(DateUtils.nowDateTimeString());
                    // 1、git 进行克隆
                    String gitDirPro = appProperties.getGitDir() + service.getProjectName().split(BACKSLASH)[0] + BACKSLASH;
                    boolean clone = gitLabService.handleProjects(service.getServiceName(), gitDirPro, service.getProjectName());
                    if (!clone) {
                        data.setReason("git clone failed");
                        return;
                    }
                    // 2. mvn 进行编译
                    boolean compile = mvnCompileClient.compile(gitDirPro + service.getServiceName());
                    if (!compile) {
                        data.setReason("mvn compile failed");
                        return;
                    }
                    sonarService.executeSonar(gitDirPro + service.getServiceName(), service.getProjectName().split(BACKSLASH)[1]);
                    //3. jacoco fetch data
                    // 获取服务的所有实例
                    List<Instance> instances = nacosFetchService.instancesByServiceName(service.getServiceName());
                    if (CollectionUtils.isEmpty(instances)) {
                        data.setReason("nacos have no instances");
                        return;
                    }
                    String jacocoExecDir = appProperties.getWorkSpace() + serviceProjectList.getUuid() + BACKSLASH
                            + Constant.EXEC + BACKSLASH + service.getProjectName().split(BACKSLASH)[0] + BACKSLASH;
                    JacocoCliEvent jacocoCliEvent = new JacocoCliEvent(jacocoExecDir, service.getServiceName());
                    for (Instance instance : instances) {
                        jacocoCliEvent.addNode(new JacocoCliEvent.Node(instance.getIp(), appProperties.getJacocoServerPort()));
                    }
                    boolean jacocoFetch = jacocoCliService.fetchExecFile(jacocoCliEvent);
                    if (!jacocoFetch) {
                        data.setReason("jacoco fetch failed");
                        return;
                    }
                    // 4.生成 jacoco report
                    String projectDir = gitDirPro + service.getServiceName();
                    String execPath = jacocoExecDir + service.getServiceName() + JACOCO_EXEC;
                    String reportPath = attachDir + service.getProjectName().split(BACKSLASH)[0] + BACKSLASH + service.getServiceName();
                    boolean report = jacocoReportService.createReport(projectDir, execPath, service.getServiceName(), reportPath, data);
                    if (!report) {
                        data.setReason("jacoco report generated failed");
                    }
                });

            }
            try {
                boolean await = cdl.await(INT4, TimeUnit.HOURS);
                if (!await) {
                    LOGGER.info("CountDownLatch cdl retain count > 0");
                }
            } catch (Exception e) {
               LOGGER.error(e.getMessage(), e);
            }
            ReportTaskManager.setDescribe(serviceProjectList.getUuid(), "Creating Excel [生成excel]");
            // 5.生成总结的excel
            ExcelUtils.write(attachDir + serviceProjectList.getUuid() + XLSX, dataModel);
            // 6.压缩生成的报告
            ReportTaskManager.setDescribe(serviceProjectList.getUuid(), "tar running [压缩目录]");
            String compressPath = appProperties.getWorkSpace() + serviceProjectList.getUuid() + BACKSLASH;
            boolean compress = reportCompressService.compress(compressPath);
            LOGGER.info("compress: {}", compress);
            ReportTaskManager.setDescribe(serviceProjectList.getUuid(), "Sender Data to " + serviceProjectList.getEmail());
            // 任务完成需要删除记录
            mailService.sendEmailWithAttachment(serviceProjectList.getEmail(), compressPath + ATTACH + TAR_SUFFIX);
            ReportTaskManager.remove(serviceProjectList.getUuid());
        }
    }


    private void submitToSub(ServiceProjectList serviceProjectList, CountDownLatch cdl, Runnable runnable) {
        subBatchReportServiceTaskThreadPool.execute(() -> {
            try {
                runnable.run();
            } catch (Exception e) {
                LOGGER.error(e.getMessage(), e);
            } finally {
                cdl.countDown();
                ReportTaskManager.addOver(serviceProjectList.getUuid());
            }
        });

    }

    private void mkdir(String path) {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

}
